/**
 * @file tiny_fs.c
 * @author 张恒宣
 * @LastEditTime 2025/5/09
 *
 * MIT License
 *
 * Copyright (c) 2025 张恒宣 luckkeymail@163.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "tiny_fs.h"
#include "tiny_thread.h"
static tiny_fs_t *fs_root = tiny_null;
static tiny_fs_info_t* fs_info_root = tiny_null;

static int __fs_add_fd(tiny_fs_operator_t *op, const char* path)
{
    static int fd = 0;
    if (!op)
    {
        return -1;
    }
    tiny_fs_info_t *info = (tiny_fs_info_t*)tiny_malloc(sizeof(tiny_fs_info_t));
    if (!info)
    {
        return -1;
    }
    tiny_close_schedule();
    info->fd = fd++;
    info->op = op;
    strcpy(info->path, path);
    if (fs_info_root)
        fs_info_root->prev = info;
    info->next = fs_info_root;
    info->prev = tiny_null;
    fs_info_root = info;
    tiny_open_schedule();
    return info->fd;
}

static tiny_fs_info_t* __fs_get_fd(int fd)
{
    if (fd < 0)
    {
        return tiny_null;
    }
    tiny_fs_info_t *info = fs_info_root;
    while (info)
    {
        if (info->fd == fd)
        {
            return info;
        }
        info = info->next;
    }
    return tiny_null;
}

void __fs_del_fd(int fd)
{
    if (fd < 0)
    {
        return;
    }
    tiny_fs_info_t *info = fs_info_root;
    
    while (info)
    {
        if (info->fd == fd)
        {
            if (info->prev)
            {
                info->prev->next = info->next;
            }
            else
            {
                fs_info_root = info->next;
            }
            if (info->next)
            {
                info->next->prev = info->prev;
            }
            tiny_free(info);
            return;
        }
        info = info->next;
    }   
}

static tiny_fs_operator_t* __fs_get_operator(const char *path)
{

    if (!path || path[0] != '/')
    {
        return tiny_null;
    }

    tiny_fs_t *fs = fs_root->next;

    char buf[33];
    buf[0] = '/';
    int i = 1;
    for (; path[i] && (i < (sizeof(buf) - 1)) && (path[i] != '/'); ++i)
    {
        buf[i] = path[i];
    }
    buf[i] = '\0';

    while (fs != tiny_null)
    {
        if (!strcmp(fs->name, buf))
        {
            return fs->op;
        }
        fs = fs->next;
    }
    return fs_root->op;
}

int tiny_mount(const char *name, tiny_fs_operator_t *op)
{
    if (!name || name[0] != '/')
    {
        return -1;
    }
    
    if (name[1] == '\0')
    {
        fs_root = (tiny_fs_t*)tiny_malloc(sizeof(tiny_fs_t));
        if (!fs_root)
        {
            return -1;
        }
        fs_root->name = name;
        fs_root->op = op;
        fs_root->style = TINY_FS_STYLE_DIR;
        fs_root->next = tiny_null;
        return 1;
    }
    else if (!fs_root)
    {
        return -2;
    }

    /* path必须是/目录下的一级目录 */
    tiny_fs_t *fs = (tiny_fs_t*)tiny_malloc(sizeof(tiny_fs_t));
    if (!fs)
    {
        return -1;
    }
    fs->name = name;
    fs->op = op;
    fs->next = fs_root->next;
    fs->style = TINY_FS_STYLE_DIR;
    fs_root->next = fs;
    
    return 1;
}


int tiny_open(const char *path, int flags, tiny_uint32_t mode)
{
    if (!path || path[0] != '/')
    {
        return -1;
    }

    tiny_fs_operator_t *op = __fs_get_operator(path);

    if (!op)
    {
        return -1;
    }


    if (op->open && op->open(path, flags, mode) > 0)
    {
        return __fs_add_fd(op, path);
    }

    return -1;

}

int tiny_close(int fd)
{
    
    if (fd < 0)
    {
        return -1;
    }
    tiny_fs_info_t *info = __fs_get_fd(fd);
    if (!info)
    {
        return -1;
    }
    
    __fs_del_fd(fd);

    int ret = -1;
    if (info->op->close)
    {
        ret = info->op->close(info->path);
    }
   
    return ret;
}

size_t tiny_write(int fd, void *buf, size_t count)
{
    if (fd < 0)
    {
        return 0;
    }

    tiny_fs_info_t *info = __fs_get_fd(fd);

    if (!info)
    {
        return 0;
    }

    if (info->op->write)
    {
        return info->op->write(info->path, buf, count);
    }
    return 0;
}

size_t tiny_read(int fd, void *buf, size_t count)
{
    if (fd < 0)
    {
        return 0;
    }
    tiny_fs_info_t *info = __fs_get_fd(fd);
    if (!info)
    {
        return 0;
    }
    if (info->op->read)
    {
        return info->op->read(info->path, buf, count);
    }
    return 0;
}

int tiny_remove(const char *path)
{
    if (!path || path[0] != '/')
    {
        return -1;
    }
    tiny_fs_operator_t *op = __fs_get_operator(path);
    if (!op)
    {
        return -1;
    }
    if (op->remove)
    {
        return op->remove(path);
    }
    return -1;
}

int tiny_rename(const char *oldpath, const char *newpath)
{
    if (!oldpath || oldpath[0] != '/' || !newpath || newpath[0] != '/')
    {
        return -1;
    }
    tiny_fs_operator_t *op = __fs_get_operator(oldpath);
    if (!op)
    {
        return -1;
    }
    if (op->rename)
    {
        return op->rename(oldpath, newpath);
    }
    return -1;
}

void tiny_scan_dir(const char *path, int (*callback)(const char *name, tiny_fs_style_t style))
{
    if (!path || path[0] != '/')
    {
        return;
    }
    tiny_fs_operator_t *op = __fs_get_operator(path);
    if (!op)
    {
        return;
    }
    if (op->scan_dir)
    {
        op->scan_dir(path, callback);
    }
    
    if (path[1] == '\0')
    {
        tiny_fs_t *fs = fs_root->next;
        while (fs)
        {
            if (callback && !callback(fs->name + 1, fs->style))
            {
                return;
            }
            fs = fs->next;
        }
    }
}


